#include "w25q16.h"
#include <stdio.h>

extern SPI_HandleTypeDef hspi1; // 假设 CubeMX 生成的 SPI 句柄为 hspi1

// 片选控制
#define W25Q16_CS_LOW()    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
#define W25Q16_CS_HIGH()   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)

// 初始化 SPI 和 W25Q16
void W25Q16_Init(SPI_HandleTypeDef *hspi) {
	uint32_t id = W25Q16_ReadID();
	printf("W25Q16 ID: 0x%06lX\n", id); // 应输出 0xEF4015
	
    W25Q16_CS_HIGH(); // 初始时取消片选
}

// 读取 JEDEC ID（制造商和设备ID）
uint32_t W25Q16_ReadID(void) {
    uint8_t cmd = W25Q16_JEDEC_ID;
    uint8_t id[3] = {0};

    W25Q16_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, 100);
    HAL_SPI_Receive(&hspi1, id, 3, 100);
    W25Q16_CS_HIGH();

    return (id[0] << 16) | (id[1] << 8) | id[2]; // 通常返回 0xEF4015（Winbond W25Q16）
}

// 读取数据
void W25Q16_ReadData(uint32_t addr, uint8_t *data, uint32_t len) {
    uint8_t cmd[4] = {
        W25Q16_READ_DATA,
        (addr >> 16) & 0xFF,
        (addr >> 8) & 0xFF,
        addr & 0xFF
    };

    W25Q16_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
    HAL_SPI_Receive(&hspi1, data, len, 1000);
    W25Q16_CS_HIGH();
}

// 缓冲区写入
void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
    uint16_t NumOfPage, NumOfSingle, Addr, count, temp;
    
    Addr = WriteAddr % SPI_FLASH_PageSize;
    count = SPI_FLASH_PageSize - Addr;
    NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
    NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
    
    if (Addr == 0) { // 地址对齐
        if (NumOfPage == 0) { // 小于一页
            W25Q16_WritePage(WriteAddr, pBuffer, NumByteToWrite);
        } else { // 大于一页
            while (NumOfPage--) {
                W25Q16_WritePage(WriteAddr, pBuffer, SPI_FLASH_PageSize);
                WriteAddr += SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            W25Q16_WritePage(WriteAddr, pBuffer, NumOfSingle);
        }
    } else { // 地址不对齐
        if (NumOfPage == 0) { // 小于一页
            if (NumOfSingle > count) { // 跨越页边界
                temp = NumOfSingle - count;
                W25Q16_WritePage(WriteAddr, pBuffer, count);
                WriteAddr += count;
                pBuffer += count;
                W25Q16_WritePage(WriteAddr, pBuffer, temp);
            } else {
                W25Q16_WritePage(WriteAddr, pBuffer, NumByteToWrite);
            }
        } else { // 大于一页
            NumByteToWrite -= count;
            NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;
            NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
            
            W25Q16_WritePage(WriteAddr, pBuffer, count);
            WriteAddr += count;
            pBuffer += count;
            
            while (NumOfPage--) {
                W25Q16_WritePage(WriteAddr, pBuffer, SPI_FLASH_PageSize);
                WriteAddr += SPI_FLASH_PageSize;
                pBuffer += SPI_FLASH_PageSize;
            }
            
            if (NumOfSingle != 0) {
                W25Q16_WritePage(WriteAddr, pBuffer, NumOfSingle);
            }
        }
    }
}

// 写入一页数据（最大 256 字节）
void W25Q16_WritePage(uint32_t addr, uint8_t *data, uint16_t len) {
    uint8_t cmd[4] = {
        W25Q16_PAGE_PROGRAM,
        (addr >> 16) & 0xFF,
        (addr >> 8) & 0xFF,
        addr & 0xFF
    };

    // 启用写操作
    W25Q16_CS_LOW();
    HAL_SPI_Transmit(&hspi1, (uint8_t[]){W25Q16_WRITE_ENABLE}, 1, 100);
    W25Q16_CS_HIGH();

    // 写入数据
    W25Q16_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
    HAL_SPI_Transmit(&hspi1, data, len, 1000);
    W25Q16_CS_HIGH();

    // 等待写入完成
    while (W25Q16_IsBusy());
}

// 擦除一个扇区（4KB）
void W25Q16_EraseSector(uint32_t addr) {
    uint8_t cmd[4] = {
        W25Q16_SECTOR_ERASE,
        (addr >> 16) & 0xFF,
        (addr >> 8) & 0xFF,
        addr & 0xFF
    };

    W25Q16_CS_LOW();
    HAL_SPI_Transmit(&hspi1, (uint8_t[]){W25Q16_WRITE_ENABLE}, 1, 100);
    W25Q16_CS_HIGH();

    W25Q16_CS_LOW();
    HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
    W25Q16_CS_HIGH();

    while (W25Q16_IsBusy());
}

// 检查忙状态
uint8_t W25Q16_IsBusy(void) {
    uint8_t status;
    W25Q16_CS_LOW();
    HAL_SPI_Transmit(&hspi1, (uint8_t[]){W25Q16_READ_STATUS_REG1}, 1, 100);
    HAL_SPI_Receive(&hspi1, &status, 1, 100);
    W25Q16_CS_HIGH();
    return (status & 0x01); // BIT0=1 表示忙
}
